/*
 * $Id: sparc_decode.c,v 1.13 2012/07/21 00:05:04 curt Exp $
 * almost purely table driven decoder for sparc.
 */

#include "decomp.h"
#include "sparc.h"

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

/*
 * these are the different ways to print out an instruction in binary.
 * a set bit denotes a space character after the corresponding bit.
 * so, we can print out the various selectors in a semi-intelligible way
 */
unsigned int formatmask[] = {
    0X00000000, /* unknown 0 */
    0X40000000, /* call 1 */
    0X42086020, /* 3REG 2 */
    0X42086000, /* 2REG+i 3 */
    0X42087048, /* membar 4 */
    0X42400000, /* sethi 5 */
    0X42087020, /* shift5 6 */
    0X42087040, /* shift6 7 */
    0X62400000, /* bicc, fbfcc 8 */
    0X62580000, /* bpcc, fbpfcc 9 */
    0X42086420, /* movr 10 */
    0X42086400, /* movri 11 */
    0X72584000, /* bpr 12 */
    0X42184000, /* fmovcc 13 */
};

/*
 * shorthand pre-definitions for the table
 */
#define S_3REG      (S_RD|S_RS1|S_RS2)
#define S_2REGI     (S_RD|S_RS1|S_DISP|SD_DISP13)
#define S_3REGCC    (S_RD|S_RS1|S_RS2|S_SETCC)
#define S_2REGICC   (S_RD|S_RS1|S_DISP|SD_DISP13|S_SETCC)
#define S_3SREG     (S_SREG|S_RS1|S_RS2)
#define S_2SREGI    (S_SREG|S_RS1|S_DISP|SD_DISP13)
#define S_2REGI5    (S_RD|S_RS1|S_DISP|SD_DISP5)
#define S_2REGI6    (S_RD|S_RS1|S_DISP|SD_DISP6)
#define S_BICC      (S_DISP|SD_DISP22|S_A|S_PCREL|S_IF|SC_BCC)
#define S_BPCC      (S_DISP|SD_DISP19|S_A|S_P|S_CC|S_PCREL|S_IF|SC_BCC)
#define S_BPR       (S_DISP|SD_DISP16|S_A|S_P|S_CC|S_PCREL|S_IF|SC_BPR)
#define S_MOVRR     (S_RD|S_RS1|S_RS2)
#define S_MOVRI     (S_RD|S_RS1|S_DISP|SD_DISP10)
#define S_SETHI     (S_DISP|SD_DISP22|S_RD|S_U32_Z)

itype instr[] = {
{ "UNKNOWN", 0, 1, 0, 0, 0 },
{ "ZEROS", 0xffffffff, 0x00000000, 0, 0, 0 },

{ "CALL",   0xc0000000, 0x40000000, 1, SD_DISP30|S_DISP|S_PCREL|S_CALL },
{ "JMPL",   0xc1f82000, 0x81c00000, 2, S_3REG|S_CALL|S_JUMP, O_ADD},
{ "JMPL",   0xc1f82000, 0x81c02000, 3, S_2REGI|S_CALL|S_JUMP, O_ADD},

{ "SETHI",  0xc1c00000, 0x01000000, 5, S_SETHI, O_LSHIFT },

{ "MEMBAR", 0xffffff80, 0x8143e000, 4, S_MEMBAR},
{ "WRY",    0xfff82000, 0x81800000, 2, S_3SREG},
{ "WRY",    0xfff82000, 0x81802000, 3, S_2SREGI},

/* XXX WRCCR WRASI WRFPRS WRGSR etc */

{ "ADD",    0xc1f82000, 0x80000000, 2, S_3REG, O_ADD},
{ "ADD",    0xc1f82000, 0x80002000, 3, S_2REGI, O_ADD},
{ "ADDcc",  0xc1f82000, 0x80800000, 2, S_3REGCC, O_ADD},
{ "ADDcc",  0xc1f82000, 0x80802000, 3, S_2REGICC, O_ADD},
{ "ADDC",   0xc1f82000, 0x80400000, 2, S_3REG, O_ADD},
{ "ADDC",   0xc1f82000, 0x80402000, 3, S_2REGI, O_ADD},
{ "ADDCcc", 0xc1f82000, 0x80c00000, 2, S_3REGCC, O_ADD},
{ "ADDCcc", 0xc1f82000, 0x80c02000, 3, S_2REGICC, O_ADD},

{ "SUB",    0xc1f82000, 0x80200000, 2, S_3REG, O_SUB},
{ "SUB",    0xc1f82000, 0x80202000, 3, S_2REGI, O_SUB},
{ "SUBcc",  0xc1f82000, 0x80a00000, 2, S_3REGCC, O_SUB},
{ "SUBcc",  0xc1f82000, 0x80a02000, 3, S_2REGICC, O_SUB},
{ "SUBC",   0xc1f82000, 0x80600000, 2, S_3REG, O_SUB},
{ "SUBC",   0xc1f82000, 0x80602000, 3, S_2REGI, O_SUB},
{ "SUBCcc", 0xc1f82000, 0x80e00000, 2, S_3REGCC, O_SUB},
{ "SUBCcc", 0xc1f82000, 0x80c02000, 3, S_2REGICC, O_SUB},

{ "SDIVX",  0xc1f82000, 0x81680000, 2, S_3REG, O_DIV},
{ "SDIVX",  0xc1f82000, 0x81682000, 3, S_2REGI, O_DIV},
{ "UDIVX",  0xc1f82000, 0x80680000, 2, S_3REG, O_DIV},
{ "UDIVX",  0xc1f82000, 0x80682000, 3, S_2REGI, O_DIV},

{ "SDIV",   0xc1f82000, 0x80780000, 2, S_3REG, O_DIV},
{ "SDIV",   0xc1f82000, 0x80782000, 3, S_2REGI, O_DIV},
{ "SDIVcc", 0xc1f82000, 0x80f80000, 2, S_3REGCC, O_DIV},
{ "SDIVcc", 0xc1f82000, 0x80f82000, 3, S_2REGICC, O_DIV},

{ "UDIV",   0xc1f82000, 0x80700000, 2, S_3REG, O_DIV},
{ "UDIV",   0xc1f82000, 0x80702000, 3, S_2REGI, O_DIV},
{ "UDIVcc", 0xc1f82000, 0x80f00000, 2, S_3REGCC, O_DIV},
{ "UDIVcc", 0xc1f82000, 0x80f02000, 3, S_2REGICC, O_DIV},

{ "MULX",   0xc1f82000, 0x80480000, 2, S_3REG, O_MUL},
{ "MULX",   0xc1f82000, 0x80482000, 3, S_2REGI, O_MUL},
{ "SMUL",   0xc1f82000, 0x80580000, 2, S_3REG, O_MUL},
{ "SMUL",   0xc1f82000, 0x80582000, 3, S_2REGI, O_MUL},
{ "SMULcc", 0xc1f82000, 0x80d80000, 2, S_3REGCC, O_MUL},
{ "SMULcc", 0xc1f82000, 0x80d82000, 3, S_2REGICC, O_MUL},

{ "UMUL",   0xc1f82000, 0x80500000, 2, S_3REG, O_MUL},
{ "UMUL",   0xc1f82000, 0x80502000, 3, S_2REGI, O_MUL},
{ "UMULcc", 0xc1f82000, 0x80d00000, 2, S_3REGCC, O_MUL},
{ "UMULcc", 0xc1f82000, 0x80d02000, 3, S_2REGICC, O_MUL},

{ "AND",    0xc1f82000, 0x80080000, 2, S_3REG, O_AND},
{ "AND",    0xc1f82000, 0x80082000, 3, S_2REGI, O_AND},
{ "ANDcc",  0xc1f82000, 0x80880000, 2, S_3REGCC, O_AND},
{ "ANDcc",  0xc1f82000, 0x80882000, 3, S_2REGICC, O_AND},
{ "ANDN",   0xc1f82000, 0x80280000, 2, S_3REG, O_AND},
{ "ANDN",   0xc1f82000, 0x80282000, 3, S_2REGI, O_AND},
{ "ANDNcc", 0xc1f82000, 0x80a80000, 2, S_3REGCC, O_AND},
{ "ANDNcc", 0xc1f82000, 0x80a82000, 3, S_2REGICC, O_AND},

{ "OR",     0xc1f82000, 0x80100000, 2, S_3REG, O_OR},
{ "OR",     0xc1f82000, 0x80102000, 3, S_2REGI, O_OR},
{ "ORcc",   0xc1f82000, 0x80900000, 2, S_3REGCC, O_OR},
{ "ORcc",   0xc1f82000, 0x80902000, 3, S_2REGICC, O_OR},
{ "ORN",    0xc1f82000, 0x80300000, 2, S_3REG, O_OR},
{ "ORN",    0xc1f82000, 0x80302000, 3, S_2REGI, O_OR},
{ "ORNcc",  0xc1f82000, 0x80b00000, 2, S_3REGCC, O_OR},
{ "ORNcc",  0xc1f82000, 0x80b02000, 3, S_2REGICC, O_OR},

{ "XOR",    0xc1f82000, 0x80180000, 2, S_3REG, O_XOR},
{ "XOR",    0xc1f82000, 0x80182000, 3, S_2REGI, O_XOR},
{ "XORcc",  0xc1f82000, 0x80980000, 2, S_3REGCC, O_XOR},
{ "XORcc",  0xc1f82000, 0x80982000, 3, S_2REGICC, O_XOR},
{ "XORN",   0xc1f82000, 0x80380000, 2, S_3REG, O_XOR},
{ "XORN",   0xc1f82000, 0x80382000, 3, S_2REGI, O_XOR},
{ "XORNcc", 0xc1f82000, 0x80b80000, 2, S_3REGCC, O_XOR},
{ "XORNcc", 0xc1f82000, 0x80b82000, 3, S_2REGICC, O_XOR},

{ "SLL",   0xc1f83000, 0x81280000, 6, S_3REG, O_LSHIFT},
{ "SLL",   0xc1f83000, 0x81282000, 6, S_2REGI5, O_LSHIFT},
{ "SRL",   0xc1f83000, 0x81300000, 6, S_3REG, O_RSHIFT},
{ "SRL",   0xc1f83000, 0x81302000, 6, S_2REGI5, O_RSHIFT},
{ "SRA",   0xc1f83000, 0x81380000, 6, S_3REG, O_RSHIFT},
{ "SRA",   0xc1f83000, 0x81382000, 6, S_2REGI5, O_RSHIFT},

{ "SLLX",  0xc1f83000, 0x81281000, 6, S_3REG, O_LSHIFT},
{ "SLLX",  0xc1f83000, 0x81283000, 7, S_2REGI6, O_LSHIFT},
{ "SRLX",  0xc1f83000, 0x81301000, 6, S_3REG, O_RSHIFT},
{ "SRLX",  0xc1f83000, 0x81303000, 7, S_2REGI6, O_RSHIFT},
{ "SRAX",  0xc1f83000, 0x81381000, 6, S_3REG, O_RSHIFT},
{ "SRAX",  0xc1f83000, 0x81383000, 7, S_2REGI6, O_RSHIFT},

{ "RESTORE", 0xc1f82000, 0x81e80000, 2, S_3REG|S_RESTORE, O_ADD},
{ "RESTORE", 0xc1f82000, 0x81e82000, 3, S_2REGI|S_RESTORE, O_ADD},

{ "SAVE",   0xc1f82000, 0x81e00000, 2, S_3REG|S_SAVE, O_ADD},
{ "SAVE",   0xc1f82000, 0x81e02000, 3, S_2REGI|S_SAVE, O_ADD},

{ "STB",   0xc1f82000, 0xc0280000, 2, S_3REG|S_STORE, O_ADD},
{ "STB",   0xc1f82000, 0xc0282000, 3, S_2REGI|S_STORE, O_ADD},
{ "STH",   0xc1f82000, 0xc0300000, 2, S_3REG|S_STORE, O_ADD},
{ "STH",   0xc1f82000, 0xc0302000, 3, S_2REGI|S_STORE, O_ADD},
{ "STW",   0xc1f82000, 0xc0200000, 2, S_3REG|S_STORE, O_ADD},
{ "STW",   0xc1f82000, 0xc0202000, 3, S_2REGI|S_STORE, O_ADD},
{ "STX",   0xc1f82000, 0xc0700000, 2, S_3REG|S_STORE, O_ADD},
{ "STX",   0xc1f82000, 0xc0702000, 3, S_2REGI|S_STORE, O_ADD},

{ "LDSB",   0xc1f82000, 0xc0480000, 2, S_3REG|S_LOAD, O_ADD},
{ "LDSB",   0xc1f82000, 0xc0482000, 3, S_2REGI|S_LOAD, O_ADD},
{ "LDSH",   0xc1f82000, 0xc0500000, 2, S_3REG|S_LOAD, O_ADD},
{ "LDSH",   0xc1f82000, 0xc0502000, 3, S_2REGI|S_LOAD, O_ADD},
{ "LDSW",   0xc1f82000, 0xc0400000, 2, S_3REG|S_LOAD, O_ADD},
{ "LDSW",   0xc1f82000, 0xc0402000, 3, S_2REGI|S_LOAD, O_ADD},

{ "LDUB",   0xc1f82000, 0xc0080000, 2, S_3REG|S_LOAD, O_ADD},
{ "LDUB",   0xc1f82000, 0xc0082000, 3, S_2REGI|S_LOAD, O_ADD},
{ "LDUH",   0xc1f82000, 0xc0100000, 2, S_3REG|S_LOAD, O_ADD},
{ "LDUH",   0xc1f82000, 0xc0102000, 3, S_2REGI|S_LOAD, O_ADD},
{ "LDUW",   0xc1f82000, 0xc0000000, 2, S_3REG|S_LOAD, O_ADD},
{ "LDUW",   0xc1f82000, 0xc0002000, 3, S_2REGI|S_LOAD, O_ADD},
{ "LDX",    0xc1f82000, 0xc0580000, 2, S_3REG|S_LOAD, O_ADD},
{ "LDX",    0xc1f82000, 0xc0582000, 3, S_2REGI|S_LOAD, O_ADD},

#ifdef notdef
/* TODO - opcodes */
{ "CASA",   0xc1f82000, 0xc0480000, 2, S_3REG|S_ASI},
{ "CASA",   0xc1f82000, 0xc0482000, 3, S_2REGI|S_ASIR},
{ "CASAX",   0xc1f82000, 0xc0500000, 2, S_3REG|S_ASI},
{ "CASAX",   0xc1f82000, 0xc0502000, 3, S_2REGI|S_ASIR},

{ "LDSBA",   0xc1f82000, 0xc0480000, 2, S_3REG|S_ASI},
{ "LDSBA",   0xc1f82000, 0xc0482000, 3, S_2REGI|S_ASIR},
{ "LDSHA",   0xc1f82000, 0xc0500000, 2, S_3REG|S_ASI},
{ "LDSHA",   0xc1f82000, 0xc0502000, 3, S_2REGI|S_ASIR},
{ "LDSWA",   0xc1f82000, 0xc0400000, 2, S_3REG|S_ASI},
{ "LDSWA",   0xc1f82000, 0xc0402000, 3, S_2REGI|S_ASIR},

{ "LDUBA",   0xc1f82000, 0xc0080000, 2, S_3REG|S_ASI},
{ "LDUBA",   0xc1f82000, 0xc0082000, 3, S_2REGI|S_ASIR},
{ "LDUHA",   0xc1f82000, 0xc0100000, 2, S_3REG|S_ASI},
{ "LDUHA",   0xc1f82000, 0xc0102000, 3, S_2REGI|S_ASIR},
{ "LDUWA",   0xc1f82000, 0xc0000000, 2, S_3REG|S_ASI},
{ "LDUWA",   0xc1f82000, 0xc0002000, 3, S_2REGI|S_ASIR},
{ "LDXA",    0xc1f82000, 0xc0580000, 2, S_3REG|S_ASI},
{ "LDXA",    0xc1f82000, 0xc0582000, 3, S_2REGI|S_ASIR},
#endif

{ "BN",     0xdfc00000, 0x00800000, 8, S_BICC},
{ "BE",     0xdfc00000, 0x02800000, 8, S_BICC},
{ "BLE",    0xdfc00000, 0x04800000, 8, S_BICC},
{ "BL",     0xdfc00000, 0x06800000, 8, S_BICC},
{ "BLEU",   0xdfc00000, 0x08800000, 8, S_BICC},
{ "BCS",    0xdfc00000, 0x0a800000, 8, S_BICC},
{ "BNEG",   0xdfc00000, 0x0c800000, 8, S_BICC},
{ "BVS",    0xdfc00000, 0x0e800000, 8, S_BICC},
{ "BA",     0xdfc00000, 0x10800000, 8, S_BICC},
{ "BNE",    0xdfc00000, 0x12800000, 8, S_BICC},
{ "BG",     0xdfc00000, 0x14800000, 8, S_BICC},
{ "BGE",    0xdfc00000, 0x16800000, 8, S_BICC},
{ "BGU",    0xdfc00000, 0x18800000, 8, S_BICC},
{ "BCC",    0xdfc00000, 0x1a800000, 8, S_BICC},
{ "BPOS",   0xdfc00000, 0x1c800000, 8, S_BICC},
{ "BVC",    0xdfc00000, 0x1e800000, 8, S_BICC},

{ "BPN",     0xdfd00000, 0x00400000, 9, S_BPCC},
{ "BPE",     0xdfd00000, 0x02400000, 9, S_BPCC},
{ "BPLE",    0xdfd00000, 0x04400000, 9, S_BPCC},
{ "BPL",     0xdfd00000, 0x06400000, 9, S_BPCC},
{ "BPLEU",   0xdfd00000, 0x08400000, 9, S_BPCC},
{ "BPCS",    0xdfd00000, 0x0a400000, 9, S_BPCC},
{ "BPNEG",   0xdfd00000, 0x0c400000, 9, S_BPCC},
{ "BPVS",    0xdfd00000, 0x0e400000, 9, S_BPCC},
{ "BPA",     0xdfd00000, 0x10400000, 9, S_BPCC},
{ "BPNE",    0xdfd00000, 0x12400000, 9, S_BPCC},
{ "BPG",     0xdfd00000, 0x14400000, 9, S_BPCC},
{ "BPGE",    0xdfd00000, 0x16400000, 9, S_BPCC},
{ "BPGU",    0xdfd00000, 0x18400000, 9, S_BPCC},
{ "BPCC",    0xdfd00000, 0x1a400000, 9, S_BPCC},
{ "BPPOS",   0xdfd00000, 0x1c400000, 9, S_BPCC},
{ "BPVC",    0xdfd00000, 0x1e400000, 9, S_BPCC},

{ "BRZ",     0xdfc00000, 0x02c00000, 12, S_BPR},
{ "BRLEZ",   0xdfc00000, 0x04c00000, 12, S_BPR},
{ "BRLZ",    0xdfc00000, 0x06c00000, 12, S_BPR},
{ "BRNZ",    0xdfc00000, 0x0ac00000, 12, S_BPR},
{ "BRGZ",    0xdfc00000, 0x0cc00000, 12, S_BPR},
{ "BRGEZ",   0xdfc00000, 0x0ec00000, 12, S_BPR},

{ "MOVRZ",    0xc1f83fe0, 0x81780400, 10, S_MOVRR},
{ "MOVRZ",    0xc1f83c00, 0x81782400, 11, S_MOVRI},
{ "MOVRLEZ",  0xc1f83fe0, 0x81780800, 10, S_MOVRR},
{ "MOVRLEZ",  0xc1f83c00, 0x81782800, 11, S_MOVRI},
{ "MOVRLZ",   0xc1f83fe0, 0x81780c00, 10, S_MOVRR},
{ "MOVRLZ",   0xc1f83c00, 0x81782c00, 11, S_MOVRI},
{ "MOVRNZ",   0xc1f83fe0, 0x81781400, 10, S_MOVRR},
{ "MOVRNZ",   0xc1f83c00, 0x81783400, 11, S_MOVRI},
{ "MOVRGZ",   0xc1f83fe0, 0x81781800, 10, S_MOVRR},
{ "MOVRGZ",   0xc1f83c00, 0x81783800, 11, S_MOVRI},
{ "MOVRGEZ",  0xc1f83fe0, 0x81781c00, 10, S_MOVRR},
{ "MOVRGEZ",  0xc1f83c00, 0x81783c00, 11, S_MOVRI},

{ 0 }

};

/*
 * extract bytes to store/load and sign extension from parts of opcode
 */
#define store_width(o) store_width_xlate[(o >> 19) & 0xf]
#define load_width(o) load_width_xlate[(o >> 19) & 0xf]
#define load_extend(o) extend_xlate[(o >> 19) & 0xf]

int store_width_xlate[] = {
    0, 0, 0, 0, 4, 1, 2, 0,
    0, 0, 0, 0, 0, 0, 8, 0
};

int load_width_xlate[] = {
    4, 1, 2, 0, 0, 0, 0, 0,
    4, 1, 2, 8, 0, 0, 0, 0
};

int extend_xlate[] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 0, 0, 0, 0, 0,
};

/*
 * given an opcode, return the index into the instruction pattern
 */
int
lookup_opcode(unsigned int opcode)
{
    int i;
    itype *itp;
    int found = 0;

#ifdef DEBUG
    /*
     * this code is only used when trying to debug the opcode table
     * itself, so we go through the entire table, looking for all
     * matches, if we get more than one, we print out a nastygram
     * and continue
     */
#define XLINE(n, v, m)							\
    printf("    0x%08x   %40s %s\n", (unsigned int)v, format_binary(v, 32, m), n)

#define IDUMP(j)							\
    printf("%d %s:\n", j, instr[j].name);				\
    XLINE("mask", instr[j].opmask, formatmask[instr[j].format]);		\
    XLINE("match", instr[j].opvalue, formatmask[instr[j].format]);		\
    XLINE("result", instr[j].opmask & opcode, formatmask[instr[j].format]);

    for (i = 0, itp = instr; itp->name; itp++, i++) {
        if ((opcode & itp->opmask) == itp->opvalue) {
            if (found) {
                printf("multimatch:\n");
                printf("    0x%08x   opcode\n\n", opcode);
                IDUMP(i);
                IDUMP(found);
            }
        }
    }
#endif

    /*
     * XXX - here is where the gnarly stuff optimizing the instruction
     * lookup lives when I write it - for now, just a highly lame linear
     * search
     */
#ifdef FASTMATCH
    /*
     * it will greatly reduce the total match time if we check the most
     * frequent instructions first.
     * we hafta maintain a runtime ordering, counting the hit frequency
     * and reordering based on the hit count.
     * a hit swaps two instructions:
     * the hit, and the first one having the same hit count as the immediate
     * predecessor.
     * it ought to be possible to abstract this out and make it portable
     * to other, and hence, lamer processor architectures like X86.
     */
#else
    /* look up the instruction in the type table */
    for (i = 0, itp = instr; itp->name; itp++, i++) {
        if ((opcode & itp->opmask) == itp->opvalue) {
            found = i;
            break;
        }
    }
#endif

    if (!itp->name) {
        i = 0;
        itp = instr;
    }

    if (verbose & VERBOSE_LOW) {
        printf("crack_inst: %x %40s\n", opcode,
                format_binary(opcode, 32, formatmask[itp->format]));
    }
    return (i);
}

/*
 * all sparc instructions are cracked here.
 *
 * if we have a pc-relative thing here, we'll generate a label for the target.
 *
 * return 0 if we are done with this linear program flow, else return bytes
 * that we processed, 4.
 *
 * most importantly, recurse when we hit a conditional branch
 */
inst *
crack_inst(section *sp, int offset, int *bytesp)
{
    inst *ip;       /* this instruction */
    inst *pip;      /* previous instruction */
    int i = 0;
    itype *itp;
    label *lp;
    reference *rp;

    unsigned int opcode;

    opcode =
	(sp->data[offset + 0] << 24) | (sp->data[offset + 1] << 16) |
	(sp->data[offset + 2] << 8) | sp->data[offset + 3];

    ip = make_inst(sp, offset);
    ip->opcode = opcode;
    *bytesp = 4;
    ip->width = 8;
    ip->extend = 0;

    ip->type = lookup_opcode(opcode);
    itp = &instr[ip->type];

    if (itp->syndrome & S_RD) {
        ip->attr[A_RD] = (opcode >> 25) & 0x1f;
    }
    if (itp->syndrome & S_RS1) {
        ip->attr[A_RS1] = (opcode >> 14) & 0x1f;
    }
    if (itp->syndrome & S_RS2) {
        ip->attr[A_RS2] = opcode & 0x1f;
    }
    if (itp->syndrome & S_A) {
        ip->attr[A_A] = (opcode >> 29) & 0x1;
    }
    if (itp->syndrome & S_P) {
        ip->attr[A_P] = (opcode >> 19) & 0x1;
    }
    if (itp->syndrome & S_CC) {
        ip->attr[A_CC] = (opcode >> 20) & 0x3;
    }
    if (itp->syndrome & S_MEMBAR) {
        ip->attr[A_MEMBAR] = opcode & 0x7f;
    }
    if (itp->syndrome & S_ASI) {
        ip->attr[A_ASI] = (opcode >> 5) & 0xff;
    }

    if (itp->syndrome & S_DISP) {
        int disp;
        switch (itp->syndrome & S_DISPMASK) {
        case SD_DISP13:
            disp = extend(opcode & 0x1fff, 13); break;
        case SD_DISP16:
            disp = extend((opcode & 0x3fff) | ((opcode >> 6) & 0xc000), 16); break;
        case SD_DISP19:
            disp = extend(opcode & 0x7ffff, 19); break;
        case SD_DISP22:
            disp = extend(opcode & 0x3fffff, 22); break;
        case SD_DISP30:
            disp = extend(opcode & 0x3fffffff, 30); break;
        case SD_DISP10:
            disp = opcode & 0x3ff; break;
        case SD_DISP5:
            disp = opcode & 0x1f; break;
        case SD_DISP6:
            disp = opcode & 0x3f; break;
        default:
            printf("crack_inst: unknown disp type %x\n", (unsigned int)(itp->syndrome & S_DISPMASK));
            break;
        }
        ip->attr[A_DISP] = disp;
    }

    if (itp->syndrome & S_COND) {
        int cond;
        switch (itp->syndrome & S_CONDMASK) {
        case SC_BCC:
            cond = (opcode >> 25) & 0xf; break;
        case SC_BPR:
            cond = (opcode >> 25) & 0x7; break;
        case SC_MOVCC:
            cond = (opcode >> 14)  & 0x1f; break;
        case SC_MOVR:
            cond = (opcode >> 10)  & 0x7; break;
        case SC_FMOVCC:
            cond = ((opcode >> 14) & 0xf) | ((opcode >> (13 - 4)) & 0x10); break;
        case SC_FMOVR:
            cond = (opcode >> 10)  & 0x7; break;
        case SC_FBFCC:
            cond = (opcode >> 25) & 0xf; break;
        case SC_FBPFCC:
            cond = (opcode >> 25) & 0xf; break;
        default:
            printf("crack_inst: unknown cond type %x\n", (unsigned int)(itp->syndrome & S_CONDMASK));
            break;
        }
        ip->attr[A_COND] = cond;
        ip->attr[A_CTYPE] = (itp->syndrome & S_CONDMASK) >> 16;
    }

    /* we have a displacement, make sure there's a label and reference */
    if (itp->syndrome & S_PCREL) {
        rp = ip->ref[0];
        int dest = (ip->attr[A_DISP] << 2) + ip->offset;
        if (!rp) {
            lp = make_label(sp, dest, 0);
            rp = make_reference(sp, offset, 0, lp);
            if (verbose & VERBOSE_OBJ) {
                printf("crack_inst: genlabel %s\n", lp->name);
            }
            rp->howcode = 0x20; /* XXX */
        }
        if (itp->syndrome & S_IF) {
            inst *branch;
            branch = lookup_inst(sp, dest);
            if (branch) {
                if (verbose & VERBOSE_FLOW) {
                    printf("crack_inst: %x branch %x already visited \n", offset, dest);
                }
            } else {
                if (verbose & VERBOSE_FLOW) {
                    printf("crack_inst: %x recurse to %x\n", offset, dest);
                }
                branch = crack_code(sp, dest);
            }
            branch->flow[branch->refcnt++] = ip;
            ip->branch = branch;
        }
    }

    if (itp->syndrome & S_RD &&
            !(itp->syndrome & (S_SAVE|S_RESTORE))) {
        ip->calc = dest_expr(ip);
    }

    if (itp->syndrome & S_STORE) {
        ip->width = store_width(opcode);
    }

    if (itp->syndrome & S_LOAD) {
        ip->width = load_width(opcode);
        ip->extend = load_extend(opcode);
    }

    if (itp->syndrome & S_SAVE) {
        if (!ip->addr) {
            printf("SAVE without label!\n");
        }
    }

    /*
     * look at the previous instruction.
     * if it was an unconditional branch of some type, we are done with this
     * code path.
     */
    if (offset == 0) {
        return (ip);
    }

    pip = sp->insts[offset - 4];

    /* if we have a previous instruction */
    if (pip) {
        itp = &instr[pip->type];
        if (itp->syndrome & S_JUMP && pip->attr[A_RD] == REG_G0) {
            if (verbose & VERBOSE_INST) {
                printf("crack_inst: branch done at %x\n", offset);
            }
            *bytesp = 0;
        }
    }
    return (ip);
}

#define RELTYPES    64
char reltype[RELTYPES] = {
    0, 0, 0, 0,  0, 0, 0, 1,    /* 00 */
    0, 7, 0, 0,  2, 0, 0, 0,
    0, 0, 0, 0,  0, 0, 0, 0,    /* 10 */
    0, 0, 0, 0,  0, 0, 0, 0,
    3, 0, 4, 5,  6, 0, 0, 0,    /* 20 */
    0, 0, 0, 0,  0, 0, 0, 0,
    0, 0, 0, 0,  0, 0, 0, 0,    /* 30 */
    0, 0, 0, 0,  0, 0, 0, 0,
};
char *relpat[] = {
    "UNKNOWN(%s)",
    "PCREL(%s)",        /* 0x07 RELOC_DISP30 */
    "LL10(%s)",         /* 0x0c RELOC_LL10 */
    "%s",               /* 0x20 RELOC_ALL */
    "HH22(%s)",         /* 0x22 RELOC_HH22 */
    "HM10(%s)",         /* 0x23 RELOC_HM10 */
    "LM22(%s)",          /* 0x24 RELOC_LM22 */
    "HI22(%s)"          /* 0x24 RELOC_HI22 */
};

char *
howtomac(int i)
{
    int index;
    char *macro;

    i = i % RELTYPES;
    index = reltype[i];
    macro = relpat[index];
    return (macro);
}

void
dump_inst(inst *ip)
{
    itype *itp;

    itp = &instr[ip->type];

    printf("    0x%08x: %08x %s", ip->offset, ip->opcode, itp->name);
    if (itp->syndrome & S_A) {
        if (ip->attr[A_A]) {
            printf(",a");
        }
    }
    if (itp->syndrome & S_P) {
        if (ip->attr[A_P]) {
            printf(",p");
        }
    }
    printf(" ");
    if (itp->syndrome & S_CC) {
        if (ip->attr[A_CC] == 0) {
            printf("%%icc, ");
        } else {
            printf("%%xcc, ");
        }
    }
    if (itp->syndrome & S_MEMBAR) {
        printf("0x%x", ip->attr[A_MEMBAR]);
    }
    if (itp->syndrome & S_RD) {
        printf("%s, ", regname[ip->attr[A_RD]]);
    }
    if (itp->syndrome & S_ASI) {
        printf("asi: 0x%02x,", ip->attr[A_ASI]);
    } else if (itp->syndrome & S_ASIR) {
        printf("asi: %%asi,");
    }

    if (itp->syndrome & S_RS1) {
        printf("%s, ", regname[ip->attr[A_RS1]]);
    }
    if (itp->syndrome & S_RS2) {
        printf("%s", regname[ip->attr[A_RS2]]);
    }
    if (itp->syndrome & S_DISP) {
        if (ip->ref[0]) {
            printf(howtomac(ip->ref[0]->howcode), ip->ref[0]->place->name);
        } else {
            int i = ip->attr[A_DISP];
            if (i & 0x80000000 && (!((itp->syndrome & S_DISPMASK) == SD_DISP22))) {
                printf("-%d", -i);
            } else {
                printf("0x%x", i);
            }
        }
    }
    if (ip->width != 8) {
        printf(" (width %d", ip->width);
        if (ip->extend) {
            printf(" extend ");
        }
        printf(")");
    }

    if (ip->type == 0 && ip->opcode != 0) {     /* unknown */
        printf(" %-40s",
	       format_binary(ip->opcode, 32, formatmask[itp->format]));
    }
    if ((verbose & VERBOSE_CALC) && ip->calc) {
        dump_expr(ip->calc, 2, "calc");
    }

    if (ip->state) {
        if (itp->syndrome & S_RD) {
            dump_expr(ip->state->reg[ip->attr[A_RD]], 1, regname[ip->attr[A_RD]]);
        }
        dumpregs(ip->state);
    }
    printf("\n");
}

/*
 * given an instruction, generate trivial rtl for it.
 * don't reference the register state. that's for the analysis pass.
 */
expr *
dest_expr(inst *ip)
{
    expr *ep;
    itype *itp;
    expr *s1, *s2;

    itp = &instr[ip->type];

    /* if we've got an immediate operand, build an appropriate expression */
    if (itp->syndrome & S_DISP) {
        if (ip->ref[0]) {
            s2 = ref_expr(ip->ref[0]);
        } else {
            s2 = make_expr(O_CONST, 0, 0, 0, ip->attr[A_DISP]);
        }
    }

    /* sethi */
    if (itp->syndrome == S_SETHI) {
       s1 = s2;
       s2 = c10_expr;
    }

    if (itp->syndrome & S_RS2) {
        s2 = reg_expr[ip->attr[A_RS2]];
    }

    if (itp->syndrome & S_RS1) {
        s1 = reg_expr[ip->attr[A_RS1]];
    }

    ep = make_expr(itp->op, s1, s2, 0, 0);
    return fold(ep);
}

/*
 * on sparc, only one reference per instruction.
 */
howtoindex(int x)
{
    return (0);
}

#ifdef UNIT_TEST
unsigned int opcodes[] = {
    0x9de3bf50,
    0x02c00000,
    0,
};

void
test()
{
    int i;
    itype *itp;
    int o;
    verbose = -1;

    for (i = 0; opcodes[i] != 0; i++) {
        o = lookup_opcode(opcodes[i]);
        itp = &instr[o];
        printf("%s\n", itp->name);
    }
    exit(0);
}
#endif